home *** CD-ROM | disk | FTP | other *** search
- =========================================================================
- RealTime.library
- =========================================================================
-
- Introduction
- ~~~~~~~~~~~~
- RealTime library is a shared library for the Amiga, designed to help
- applications synchronize to one another. An example of this is an animation
- player which could synchronize to a music program; Or, both of them could
- be sync'd to an external source such as SMPTE time code.
- RealTime allows any number of timing contexts, or "conductors", so
- called because of their similarity to the conductor of an orchestra.
- RealTime's function is essentially to function as a metronome or master
- clock, distributing a continuous stream of timing information to different
- tasks.
-
- Goals
- ~~~~~
- The goals of RealTime are:
-
- 1. To encourage development of music software and synchronized
- multimedia applications by providing a working driver to the public.
-
- 2. To spur the development of applications which allow the user to
- create and control multimedia presentations.
-
- Operating System Requirements
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The RealTime library functions identically under 2.0 or 1.3. However,
- many of the various utilities that come with RealTime require GadTools or
- other 2.0 features.
-
- The Conductor System
- ~~~~~~~~~~~~~~~~~~~~
- RealTime maintains a list of conductors and sends timing information to
- individual "players" (like the musicians in an orchestra). There can be any
- number of conductors, and each represents a seperate timing context, just
- like real musicians playing in different concert halls don't have to stay
- in time with each other. Similarly, there can be any number of players.
- An application will usually create a single PlayerInfo structure
- (using the CreatePlayer() call) and the attach it to a conductor. (If the
- conductor does not yet exist, it will automatically be created). In some
- cases, an application might want to create more than one player, when
- multiple independent timing contexts are required.
- The conductors are driven by a central clock in RealTime called the
- "heartbeat", which is driven by the Amiga's timer chips at a rate of 600
- ticks per second. The heartbeat clock pulses are then distributed to the
- conductors, which are then distributed to the various players. (Historical
- note: The RealTime Heartbeat was originally 500 Hz (2 ms per tick),
- however a number of people requested that the tick rate be an even
- multiple of the NTSC, PAL and motion picture frame rates (30, 25 and 24
- Hz, respectively)).
- Each conductor keeps track of the variables that pertain to a
- particular timing context. These include:
-
- -- the state of the conductor, i.e. running, stopped, paused,
- locating, etc.
- -- what time it is, relative to some agreed-upon "start time".
- -- whether the conductor is being driven by an external source such as
- SMPTE.
-
- Time Formats
- ~~~~~~~~~~~~
- Often times, and application will want to receive timing information
- in its own particular format. Particularly, a music program might want to
- scale the current clock time based on the current tempo setting, a video
- application might want to format the time into SMPTE format, etc. RealTime
- provides a way for applications to attach an application-specific
- conversion function to their player.
- In addition, there is an added complexity having to do with music
- programs. RealTime itself only deals in 600Hz ticks -- it has no
- understanding of things such as tempo, accelerando, ritardando, rubato,
- etc. It is up to the application to provide these features, by scaling the
- incoming clock times based on its current internal variables. The
- suggested method for doing this is to use delta-times -- i.e. take the
- current conductor time, subtract the previous conductor time, multiply or
- divide that by the current tempo scaling factor, and add that to the
- musical time counter. Of course, the program must save the remainder and
- add it into the calculation for the next time if they want any sort of
- long term accuracy (with this method, you can get very long term accuracy
- needed for motion picture work).
- A special field has been provided in the PlayerInfo structure for
- storing application- specific time, called pi_MetricTime (Issue: May be
- changed to pi_ApplicationTime or pi_FormattedTime).
- A second complexity occurs when more than one music application is
- playing. Since each application will be using a different algorithm for
- computing tempo, it is likely that music applications would soon go out of
- sync, even though they are being fed the same clock pulses. For example,
- one program might support accelerando (the ability to gradually play
- faster and faster), while another might only support immediate changes to
- the playback rate.
- In order to resolve this problem, each Conductor contains a way for
- musically-aware applications to distribute musical timing information
- between themselves, in addition to the non-musical timing information
- provided by RealTime. The conductor contains a field called cdt_Metronome,
- which is used to store the current musical time (in 480ths of a quarter
- note [Issue: I think this is a good number]). For each conductor,
- RealTime will attempt to find the first musically-aware application on
- its PlayerInfo list. (Note: The application must indicate that it is
- musically aware by setting the PLAYER_Conducted tag attribute). The first
- application so found is designated as the "Maestro" and gets to set the
- musical time for all other musically aware applications. It is the duty of
- the Maestro to fill in the cdt_Metronome so that other musically aware
- applications can use it.
- Since the PlayerInfo list is kept in order by node priority,
- applications which are more sophisticated about tempo should set their
- node priority to a higher number so that they can get to be the Maestro,
- thus allowing the other applications to take avantage of their superior
- features (Optionally, the player priority could be user-settable). Here
- are some suggestions for PlayerInfo priorities:
-
- less than zero -- limited control of tempo (slow, fast, etc).
- 0-9 -- single-rate tempo (no tempo changes)
- 10-19 -- supports instantaneous tempo changes
- 20-29 -- supports accelerando / deccelarando
- 30-39 -- supports rubato, "human" tempos, or special
- effects such as playing at half-speed, etc.
-
- Once the cdt_Metronome has been filled in, a special bit,
- CONDUCTF_METROSET, will be set indicating that the metronome has been set.
- Then other applications can check to see if the bit is set and use the
- time -- if the bit is not set, then they must be the Maestro, so they must
- set the bit and fill in the cdt_Metronome.
- What this means is that any application that looks at cdt_Metronome,
- no matter how simple, must tell RealTime that it is musically aware (using
- the PLAYER_Conducted tag), and must take responsibility for the fact that
- it may end up being the Maestro.
-
- Negative Time
- ~~~~~~~~~~~~~
- Note that all time formats are allowed to be negative. There are a
- number of cases where a negative time might occur:
- -- A SMPTE reader is hooked up and it contains an "offset" feature,
- but the videotape has been rewound to a point before the offset.
- -- A musical application which allows a "count-down" before the music
- actually starts.
- However, none of the time formats may go backwards while the clock is
- running. They must proceed monotonically forward, if they move at all.
-
- Ready Bits
- ~~~~~~~~~~
- Each PlayerInfo maintains a "ready bit" which is used to tell RealTime
- that the player is ready to receive ticks. This bit is reset each time
- that RealTime relocates the clock to a new time. The reason for the ready
- bit is that some application need to find the appropriate location in the
- score or sequence list before they can start playing. In some modes,
- RealTime will wait until all the players are ready before starting the
- clock. In other modes RealTime will start the clock immediately, but won't
- send any clock pulses to a player that is not ready. Which of these two
- cases occurs is up to the application which actually caused the clock to
- start, however in practice it usually depends on whether the clock was
- started by the user (who can afford to wait a moment), or by an external
- timing source (which might not be capable of waiting).
- Players can change the state of their ready bit by using the
- PLAYER_Ready tag attribute.
-
- Conductor States
- ~~~~~~~~~~~~~~~~
- Conductors have basically 4 states:
-
- CLOCKSTATE_STOPPED: The clock is not running.
- CLOCKSTATE_PAUSED: From RealTime's point of view, this is identical to
- being stopped; However, some applications may wish to make a
- distinction between the two states.
- CLOCKSTATE_LOCATING: Tells RealTime to start the clock, but only when
- all players are ready. When all players are ready, RealTime will
- then automatically go into Running mode. Players can change
- their "ready" state using the PLAYER_Ready tag attribute.
- CLOCKSTATE_RUNNING: The clock is started, and time pulses are being
- distributed to any applications are ready. Note that if the
- clock is set to "running" mode directly (without going to
- locate mode) then the applications will have to "catch up" as
- best they can (like a man chasing after a train leaving the
- station). This happens all the time in music applications and
- is normally not a problem -- generally what happens is that the
- applications play silently to themselves (perhaps sending out
- control information to make sure that external devices are in
- the right state when we start playing for real), and then start
- playing for real when they finally catch up.
-
- Any player may change the conductor state at any time by calling:
-
- SetConductorState(playerinfo, newstate, clocktime);
-
- (Clocktime is ignored for stopped and paused states).
-
- There is a special "fifth state" that is not really a state, called
- CLOCKSPECIAL_METRIC. When you call SetConductorState() with this state,
- what RealTime does is send a message to the Maestro (i.e. the ranking
- musically-aware application) asking it to locate in musical time, rather
- than in clock ticks. The Maestro will use its internal tempo conversion
- routine, tempo map, or whatever application specific method it uses, to
- convert the time from metronome units to 600 Hz RealTime clock units. Then
- the Maestro should set the clock state to CLOCKSTATE_LOCATE with the newly
- computed time.
- What this does is allows any musically-aware application to locate the
- clock to a particular measure or note, yet still utilize the superior
- timing features of the current Maestro application. For example, you may
- want to start playing "at the third measure". RealTime has no concept of
- "measures", so it's up to the maestro to tell RealTime what that means in
- terms of actual clock time.
- Note that this will probably take slightly longer than a regular
- locate, because the Maestro may need to do an internal locate first to do
- the time computation.
-
- Creating a Conductor
- ~~~~~~~~~~~~~~~~~~~~
- Conductors are created automatically whenever any player attempts to
- refer to them, and are deleted whenever the number of players referring to
- them falls to zero.
-
- Creating a Player
- ~~~~~~~~~~~~~~~~~
- To create a player, you'll need to call CreatePlayerA(), which takes a
- taglist of options that specify the name of the player, the name of the
- conductor to attach it to (or create if it doesn't already exist), the
- priority of the player in the player list, etc. There are also tag items
- to set the player ID and UserData fields, which are available for
- application-specific use.
- The PLAYER_ID attribute deserves special attention. This can be used
- by applications to distinguish one player from another, which might be
- useful in cases where an application has more than one PlayerInfo. For
- example, a music sequencer might want a normal player for playing music,
- and a special player attached to a "private" conductor (see the autodocs
- on how to do this). The private conductor would be used to provide timing
- for the program's internal features such as timeouts, feedback notes, and
- other things that normally would not be synchronized to other
- applications. (Private conductors are basically used for anything that you
- would normally want to use an Amiga CIA timer for.)
-
- A player's attributes can be changed later by calling SetPlayerAttrs().
- In addition, SetPlayerAttrs is also used for a number of other things, for
- example to set the ready bit for the player.
-
- Players are deleted by calling DeletePlayer();
-
- Alarms
- ~~~~~~
- Each player has an "alarm" which will cause a signal to go off when
- the application reaches a specified time. Note that the alarm field is
- checked against the pi_MetricTime, not the conductor time, which means
- that the alarm can be in the application's own format. (Note that if there
- is no player hook, the conductor time will be copied into the
- pi_MetricTime field so that alarms still work).
- Alarms are set using the PLAYER_SignalTask, PLAYER_AlarmSigBit,
- PLAYER_AlarmTime and PLAYER_AlarmOn tags.
-
- Player Hooks
- ~~~~~~~~~~~~
- Players can have a callback hook which is invoked at each clock tick,
- but only while the clock is running, and only if the player is ready
- (there are some other criterion as well, for example if the clock has
- somehow advances beyond external sync it will freeze until the external
- time source can catch up). The hook is set up using the PLAYER_Hook tag.
- A properly written player hook will have a number of tasks to perform.
- First off, there are three possible "messages", or event types, which can
- invoke the player hook, and each carries a different data structure and
- must be handled differently:
-
- -- clock ticks (indicated by PM_TICK method)
- -- clock state changes (indicated by PM_STATE method)
- -- clock special locate (inidicated by PM_POSITION method)
-
- Here is an overview of how a player hook which supports all possible
- features might look:
-
- determine type of message:
-
- case PM_TICK:
-
- get the current tick number from the hook message
-
- if we are a musically-aware application:
-
- If CONDUCTF_METROSET is set
- get the time from cdt_Metronome.
- else
- convert the tick to musical time (perhaps using
- delta-time calculations), and place in
- cdt_Metronome.
-
- set the CONDUCTF_METROSET flag
- endif
-
- convert the cdt_Metronome time, or the current tick number
- (whichever is preferred by the application) to application-
- specific format and place in pi_MetricTime.
-
- else
-
- convert tick number to application-specific format
- (perhaps using delta-time calculations) and place in
- pi_MetricTime.
-
- endif
-
- case PM_STATE:
-
- signal application main task that we changed state
-
- case PM_POSITION:
-
- from the hook message, get the musical time to locate to and
- save in a variable.
- signal main task that a "special locate" has been requested.
-
- External Synchronization
- ~~~~~~~~~~~~~~~~~~~~~~~~
- Each RealTime conductor has a special external sync input, which can
- be used to force the conductor to synchronize with an external timing
- source.
- Applications which want to feed external time pulses into a Conductor
- must also be a player, so that the conductor will remain in existence.
- However, some specialized applications (such a SMPTE reader utility) will
- not need all the features of a full PlayerInfo, so a special tag,
- PLAYER_Quiet, has been defined which allows for a "placeholder" PlayerInfo
- to be created. This inactive player will always be skipped over during
- time distribution.
- The second thing required is that the application must inform the
- Conductor that it wishes to take over. This is done by setting the
- PLAYER_ExtSync attribute using SetPlayerAttrs(). This call is neccessary
- because there can only be one external sync source per Conductor, and
- RealTime needs to arbitrate these. (Note that the call can fail).
- Finally, the function must call ExternalSync() each time it gets an
- external sync call. ExternalSync has three parameters:
- -- the address of the player
- -- the current time (in 600 Hz units)
- -- the estimated time of the next external clock tick.
-
- The reason for the third parameter is to allow the Conductor to
- interpolate between external ticks. For example, suppose you have a SMPTE
- reader which has external ticks coming in a 30 frames per second. Suppose
- that frame #15 has just occured, which corresponds to 300 RealTime clock
- ticks. Since each external clock tick (1/30th of a second) corresponds to
- 20 RealTime clock ticks, the estimated time of the next external clock
- tick is 320.
- What this does is allow the conductor to run at its normal 600 Hz
- rate, however the clock values are clamped between the values 300 and 320.
- (with the stipulation that the clock can never go backwards if it's
- already too far ahead). Thus, the applications get the benefit of 2 ms
- timing accuracy, but can still maintain synchronization with the external
- source.
-
- Note that since the external sync application as a PlayerInfo, it can
- start and stop the clock. This feature is primarily designed for devices
- such as SMPTE readers -- the sync driver could have a feature to allow the
- clock to be started automatically whenever the sync driver senses the tape
- is moving. Thus, all the user has to do is press "play" on the videotape
- player, and all of his music and animations start playing along
- automatically, and in perfect synchronization.
-
- One other thing to note is that even when the clock is in "running"
- state, and all the applications are ready, the Conductor will not
- distribute any clock ticks until the first external clock tick is
- received. This prevents "false starts" and other inconvenient effects.
-
- Credits
- ~~~~~~~
- RealTime was envisioned by David Joiner (a.k.a. Talin) and implemented
- by Joe Pearce. Bill Barton also made a number of important suggestions.
-